package com.sz.common.core.system.service.impl;

import com.sz.common.base.constants.UserActions;
import com.sz.common.base.dao.Pagination;
import com.sz.common.base.dao.ParamData;
import com.sz.common.base.exception.Exceptions;
import com.sz.common.base.service.AbstractService;
import com.sz.common.base.utils.BeanUtils;
import com.sz.common.base.utils.StringUtils;
import com.sz.common.core.service.PrincipalLogUtils;
import com.sz.common.core.service.role.IRoleMemberService;
import com.sz.common.core.service.role.RoleMembership;
import com.sz.common.core.constants.SysErrorCodes;
import com.sz.common.core.constants.ModuleNames;
import com.sz.common.core.system.entity.Role;
import com.sz.common.core.system.service.AuthorizationService;
import com.sz.common.core.system.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Function: UserRoleService <br>
 * Date: 2016-10-27 10:51:25 <br>
 * Author: penghui.zheng <br>
 */
@Service("roleService")
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class RoleServiceImpl extends AbstractService implements RoleService {

    public static final int RO = 1;
    public static final int RC = 2;
    public static final int RV = 3;
    private List<IRoleMemberService> roleMemberServices;


    @Autowired
    AuthorizationService authorizationService;

    protected List<IRoleMemberService> getRoleMemberServices() {
        if (roleMemberServices == null) {
            roleMemberServices = BeanUtils.getBeansByType(IRoleMemberService.class);
            if (roleMemberServices == null || roleMemberServices.size() == 0) {
                throw new RuntimeException("cannot find instance of IRoleMemberService");
            }
        }
        return roleMemberServices;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public int saveRole(Role role) {
        ParamData pd = new ParamData();
        pd.put("name", role.getName());
        pd.put("code", role.getCode());
        pd.put("domainId", role.getDomainId());
        // 检查数据不能重复
        Role roleExist = (Role) dao.findForObject("RoleMapper.findByRoleName", pd);
        if (roleExist != null) {
            throw Exceptions.bizException(SysErrorCodes.ERROR_MODEL_ROLE_ROLENAME_EXIST);
        }
        roleExist = (Role) dao.findForObject("RoleMapper.findByRoleCode", pd);
        if (roleExist != null) {
            throw Exceptions.bizException(SysErrorCodes.ERROR_MODEL_ROLE_ROLECODE_EXIST);
        }
        Integer count = (Integer) dao.findForObject("RoleMapper.findRoleCount", pd);
        role.setParentId(0);
        role.setIsDeleted(false);
        role.setIsSystem(false);
        role.setDomainId(role.getDomainId());
        role.setOrdinal(count + 1);
        dao.save("RoleMapper.save", role);
        //保存操作日志
        Map<String, Object> dataBackup = new HashMap<>();
        dataBackup.put("ADD_ROLE", role);
        PrincipalLogUtils.addOperationLog(ModuleNames.SYS, ModuleNames.SYS_ROLE, UserActions.ADD, "新建角色", dataBackup);
        return role.getId();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void deleteRoleById(int roleId) {
        Role role = (Role) dao.findForObject("RoleMapper.findByRoleId", roleId);
        if (role == null) {
            throw Exceptions.bizException(SysErrorCodes.ERROR_MODEL_ROLE_NOT_FOUND, roleId);
        }
        if (role.getIsDeleted()) {
            throw Exceptions.bizException(SysErrorCodes.ERROR_MODEL_ROLE_HAS_BEEN_DELETED, roleId);
        }
        dao.update("RoleMapper.deleteRoleById", roleId);

        List<IRoleMemberService> roleMemberServices = getRoleMemberServices();
        for (IRoleMemberService service : roleMemberServices) {
            service.assignRoleMembers(roleId, null);
        }
        authorizationService.deleteAuthByRoleId(roleId);
        //保存操作日志
        Map<String, Object> dataBackup = new HashMap<>();
        dataBackup.put("DELETE_ROLE", role);
        PrincipalLogUtils.addOperationLog(ModuleNames.SYS, ModuleNames.SYS_ROLE, UserActions.DELETE, "删除角色", dataBackup);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void dragRole(int roleId, int targetPreId, int domainId) {
        if (targetPreId == 0) {
            return;
        }
        //保存操作日志
        String log = String.format("将角色" + roleId + "%s%s%s", "拖动到", targetPreId, "后");
        PrincipalLogUtils.addOperationLog(ModuleNames.SYS, ModuleNames.SYS_ROLE, UserActions.UPDATE,
                log, null);
        // 源orgid
        Role orgidRole = (Role) dao.findForObject("RoleMapper.findByRoleId", roleId);
        // 拖动到目的地的
        Role targetRole = (Role) dao.findForObject("RoleMapper.findByRoleId", targetPreId);
        // 源的排序序号
        int orgidOrder = orgidRole.getOrdinal();
        // 目的的排序序号
        int targetOrder = targetRole.getOrdinal();
        ParamData pd = new ParamData();
        pd.put("domainId", domainId);
        // 所有Role列表
        List<Role> list = dao.findForList("RoleMapper.roleList", pd, Role.class);
        if (orgidOrder > targetOrder) { // 向上拖
            List<Role> newList = new ArrayList<>();
            if (list != null && list.size() > 0) {
                for (int i = 0; i < list.size(); i++) {
                    Role role = list.get(i);
                    int order = list.get(i).getOrdinal();
                    if (targetOrder <= order && order <= orgidOrder) {
                        // 当排序字段在要拖动的和拖动目的地之间的需要变动，其他的不需要改变
                        if (order == targetOrder) { // 当前的等于目的，因为传的是前一个的id，所以不需要改变
                            Role newRole = new Role();
                            newRole.setId(targetRole.getId());
                            newRole.setOrdinal(targetOrder + 1);
                            newList.add(newRole);
                        } else if (order == orgidOrder) {
                            // 当前等于要拖动时，直接给他（目的+1），因为传的是前一个的id，所以+1
                            role.setOrdinal(targetOrder);
                            newList.add(role);
                        } else { // 当前等于源和目的之间直接+1
                            role.setOrdinal(order + 1);
                            newList.add(role);
                        }
                    } else {
                        newList.add(role);
                    }
                }
            }
            dao.batchUpdate("RoleMapper.updateById", newList);
        } else if (orgidOrder < targetOrder) { // 向下拖
            List<Role> nList = new ArrayList<>();
            if (list != null && list.size() > 0) {
                for (int i = 0; i < list.size(); i++) {
                    Role role = list.get(i);
                    int order = list.get(i).getOrdinal();
                    if (orgidOrder <= order && order <= targetOrder) {
                        // 当排序字段在要拖动的和拖动目的地之间的需要变动，其他的不需要改变
                        if (order == targetOrder) { // 当前的等于源时，直接给目的的排序顺序
                            Role newRole = new Role();
                            newRole.setId(targetRole.getId());
                            newRole.setOrdinal(targetOrder);
                            nList.add(newRole);
                        } else if (order == orgidOrder) { // 当前的等于目的时，直接-1，上移
                            Role newRole = new Role();
                            newRole.setId(orgidRole.getId());
                            newRole.setOrdinal(targetOrder - 1);
                            nList.add(newRole);
                        } else { // 中间都-1,上移
                            role.setOrdinal(order - 1);
                            nList.add(role);
                        }
                    } else {
                        nList.add(role);
                    }
                }
            }
            dao.batchUpdate("RoleMapper.updateById", nList);
            //保存操作日志
            //String log=String.format("将角色"+roleId+"%s%s%s","拖动到",targetPreId,"后");          
            //PrincipalLogUtils.addOperationLog(ModuleNames.SYS, ModuleNames.SYS_ROLE, UserActions.UPDATE,
            //		log, null);
        } else {
            return;
        }

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public int updateRole(Role role) {
        ParamData pd = new ParamData();
        pd.put("name", role.getName());
        pd.put("code", role.getCode());
        pd.put("domainId", role.getDomainId());
        pd.put("id", role.getId());
        // 检查数据不能重复
        Role roleExist = (Role) dao.findForObject("RoleMapper.findByRoleName", pd);
        if (roleExist != null) {
            throw Exceptions.bizException(SysErrorCodes.ERROR_MODEL_ROLE_ROLENAME_EXIST);
        }
        roleExist = (Role) dao.findForObject("RoleMapper.findByRoleCode", pd);
        if (roleExist != null) {
            throw Exceptions.bizException(SysErrorCodes.ERROR_MODEL_ROLE_ROLECODE_EXIST);
        }
        int flag = (int) dao.update("RoleMapper.update", role);
        //保存操作日志
        Map<String, Object> dataBackup = new HashMap<>();
        dataBackup.put("UPDATE_ROLE", role);
        PrincipalLogUtils.addOperationLog(ModuleNames.SYS, ModuleNames.SYS_ROLE, UserActions.UPDATE, "修改角色", dataBackup);
        return flag;
    }

    @Override
    public Role findByRoleId(int roleId) {
        Role role = (Role) dao.findForObject("RoleMapper.findByRoleId", roleId);
        return role;
    }

    /**
     * 校验角色名称
     *
     * @param name 名称
     */
    @Override
    public void checkRoleName(String name, int id, String domainId) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", name);
        map.put("domainId", domainId);
        if (id > 0) {
            map.put("id", id);
        }
        Role roleExist = (Role) dao.findForObject("RoleMapper.findByRoleName", map);
        if (roleExist != null) {
            throw Exceptions.bizException(SysErrorCodes.ERROR_MODEL_ROLE_ROLENAME_EXIST);
        }
    }

    /**
     * 校验角色编号
     *
     * @param code
     * @param domainId
     */
    @Override
    public void checkRoleCode(String code, int id, String domainId) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", code);
        map.put("domainId", domainId);
        if (id > 0) {
            map.put("id", id);
        }
        Role roleExist = (Role) dao.findForObject("RoleMapper.findByRoleCode", map);
        if (roleExist != null) {
            throw Exceptions.bizException(SysErrorCodes.ERROR_MODEL_ROLE_ROLECODE_EXIST);
        }
    }

    /**
     * 获取角色的列表
     *
     * @see RoleService#roleListPage(String,
     * int, int, int)
     */

    @Override
    public List<ParamData> roleListPage(String q, int pageIndex, int pageSize, int domainId) {
        ParamData pd = new ParamData();
        q = StringUtils.replaceSqlPattern(q);
        pd.put("q", q);
        pd.put("domainId", domainId);
        Pagination page = new Pagination();
        page.setCurrentPage(pageIndex);
        page.setPageSize(pageSize);
        pd.setPagination(page);
        List<ParamData> list = dao.findForList("RoleMapper.roleListPage", pd, ParamData.class);
        return list;
    }

    /**
     * 给角色分配角色
     *
     * @param roleId
     * @param memberships
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void assignRoleMembers(int roleId, List<RoleMembership> memberships) {
        Role role = (Role) dao.findForObject("RoleMapper.findByRoleId", roleId);
        if (role == null) {
            throw Exceptions.bizException(SysErrorCodes.ERROR_MODEL_ROLE_NOT_FOUND, roleId);
        }
        if (memberships == null) {
            return;
        }
        Map<IRoleMemberService, List<RoleMembership>> serviceMap = checkRoleMember(memberships);
        serviceMap.forEach((service, members) -> service.assignRoleMembers(roleId, members));

        //USER(1), SUPPLIER(2), CUSTOMER(3), ORGANIZATION(4), USER_GROUP(5);
        List<IRoleMemberService> services = this.getRoleMemberServices();
        if (memberships.size() == 0) {
            for (IRoleMemberService roleMemberService : services) {
                roleMemberService.assignRoleMembers(roleId, null);
            }
        } else {
            for (IRoleMemberService roleMemberService : services) {
                final boolean[] isEmpty = {true};
                memberships.stream()
                        .filter(roleMemberService::isMembershipAcceptable)
                        .forEach(membership -> isEmpty[0] = false);
                if (isEmpty[0]) {
                    roleMemberService.assignRoleMembers(roleId, null);
                }
            }
        }
        //保存操作日志
        Map<String, Object> dataBackup = new HashMap<>();
        dataBackup.put("ADD_ROLE", memberships);
        String log = String.format("给角色" + roleId + "%s", "分配成员");
        PrincipalLogUtils.addOperationLog(ModuleNames.SYS, ModuleNames.SYS_ROLE, UserActions.ADD, log, dataBackup);
    }

    /**
     * 角色删除成员
     *
     * @param roleId
     * @param memberships
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void removeRoleMembers(int roleId, List<RoleMembership> memberships) {
        Role role = (Role) dao.findForObject("RoleMapper.findByRoleId", roleId);
        if (role == null) {
            throw Exceptions.bizException(SysErrorCodes.ERROR_MODEL_ROLE_NOT_FOUND, roleId);
        }
        if (memberships == null) {
            return;
        }
        Map<IRoleMemberService, List<RoleMembership>> serviceMap = checkRoleMember(memberships);
        serviceMap.forEach((k, v) -> k.removeRoleMembers(roleId, v));
        //保存操作日志
        Map<String, Object> dataBackup = new HashMap<>();
        dataBackup.put("DELETE_ROLE", memberships);
        String log = String.format("删除角色" + roleId + "%s", "下的成员");
        PrincipalLogUtils.addOperationLog(ModuleNames.SYS, ModuleNames.SYS_ROLE, UserActions.DELETE, log, dataBackup);
    }

    /**
     * 自动生成角色编号
     */
    @Override
    public Role getRoleCode(int domainId) {
        Role role = new Role();
        int maxId = (int) dao.findForObject("RoleMapper.findMaxId", domainId);
        int id = maxId + 1;
        String code = null;
        String roleCode = null;
        if (domainId == RO) {
            code = "RO";
        } else if (domainId == RC) {
            code = "RC";
        } else if (domainId == RV) {
            code = "RV";
        }
        roleCode = code + id;
        roleCode = roleCode(roleCode, domainId, id, code);
        role.setCode(roleCode);
        return role;
    }

    /**
     * @param roleMembership
     * @return
     */
    @Override
    public List<Role> getRoleByMember(RoleMembership roleMembership) {
        List<IRoleMemberService> services = this.getRoleMemberServices();
        List<Role> allRoles = new ArrayList<>();
        services.forEach(
                service -> {
                    if (service.isMembershipAcceptable(roleMembership)) {
                        List<Role> roles = service.getRolesByMember(roleMembership);
                        if (roles != null && roles.size() > 0) {
                            allRoles.addAll(roles);
                        }
                    }
                });
        return allRoles;
    }

    /**
     * 生成角色编码
     *
     * @param roleCode 角色编号
     * @param domainId 域
     * @param id       角色id
     * @param code     编码
     * @return
     */
    private String roleCode(String roleCode, int domainId, int id, String code) {
        String rcode = null;
        ParamData pd = new ParamData();
        pd.put("code", roleCode);
        pd.put("domainId", domainId);
        Role roleExist = (Role) dao.findForObject("RoleMapper.findByRoleCode", pd);
        if (roleExist == null) {
            rcode = roleCode;
        } else {
            id = id + 1;
            rcode = code + id;
            rcode = roleCode(rcode, domainId, id, code);
        }
        return rcode;
    }


    private Map<IRoleMemberService, List<RoleMembership>> checkRoleMember(List<RoleMembership> memberships) {
        Map<IRoleMemberService, List<RoleMembership>> map = new HashMap<>();
        if (memberships != null && memberships.size() > 0) {
            List<IRoleMemberService> services = this.getRoleMemberServices();
            for (IRoleMemberService roleMemberService : services) {
                for (RoleMembership roleMembership : memberships) {
                    if (roleMemberService.isMembershipAcceptable(roleMembership)) {
                        List<RoleMembership> members = map.get(roleMemberService);
                        if (members == null) {
                            members = new ArrayList<>();
                            map.put(roleMemberService, members);
                        }
                        members.add(roleMembership);
                    }
                }
            }
        }
        return map;
    }

    @Override
    protected String getMapperNamespace() {
        return "";
    }
}
